<script setup>
import { ref, watch, onMounted } from 'vue'
import CategoryTree from '../components/CategoryTree.vue'
import SankeyChart from '../components/charts/SankeyChart.vue'
import WordCloud from '../components/charts/WordCloud.vue'
import { 
  getCategorySankey, 
  getCategoryKeywords, 
  getCategoryHealth 
} from '../api/book'

// 确保初始化为包含 level1 属性的对象
const selectedCategory = ref({
  level1: '',
  level2: '',
  level3: ''
})

const sankeyData = ref({ nodes: [], links: [] })
const keywords = ref([])
const healthData = ref({
  product_count: 0,
  update_frequency: 0,
  price_competitiveness: 0,
  health_score: 0,
  metrics: []
})

const fetchSankeyData = async () => {
  try {
    const res = await getCategorySankey()
    sankeyData.value = res.data
  } catch (error) {
    console.error('获取桑基图数据失败:', error)
  }
}

const fetchKeywords = async () => {
  // 添加更严格的判断条件
  if (!selectedCategory.value || !selectedCategory.value.level1) {
    keywords.value = []
    return
  }
  
  try {
    const res = await getCategoryKeywords({ 
      level: '1', 
      category: selectedCategory.value.level1 
    })
    keywords.value = res.data
  } catch (error) {
    console.error('获取关键词失败:', error)
  }
}

const fetchHealthData = async () => {
  // 添加更严格的判断条件
  if (!selectedCategory.value || !selectedCategory.value.level1) {
    healthData.value = {
      product_count: 0,
      update_frequency: 0,
      price_competitiveness: 0,
      health_score: 0,
      metrics: []
    }
    return
  }
  
  try {
    const res = await getCategoryHealth({ 
      level: '1', 
      category: selectedCategory.value.level1 
    })
    healthData.value = res.data
  } catch (error) {
    console.error('获取健康度数据失败:', error)
  }
}

// 添加更安全的 watch
watch(() => selectedCategory.value?.level1, () => {
  fetchKeywords()
  fetchHealthData()
})

onMounted(() => {
  fetchSankeyData()
})
</script>
import { ref, watch, onMounted } from 'vue'
import { getCategorySankey } from '../api/book'

const sankeyData = ref({ nodes: [], links: [] })

const fetchSankeyData = async () => {
  try {
    const res = await getCategorySankey()
    const rawData = res.data
    
    // 清洗数据，确保没有循环
    const cleanedData = cleanSankeyData(rawData)
    sankeyData.value = cleanedData
  } catch (error) {
    console.error('获取桑基图数据失败:', error)
  }
}

// 清洗桑基图数据，确保无循环
const cleanSankeyData = (data) => {
  const nodes = [...data.nodes]
  const links = [...data.links]
  
  // 1. 移除自循环的链接
  const filteredLinks = links.filter(link => link.source !== link.target)
  
  // 2. 检测并处理循环依赖
  const graph = buildGraph(nodes, filteredLinks)
  detectAndBreakCycles(graph, filteredLinks)
  
  return {
    nodes,
    links: filteredLinks
  }
}

// 构建图结构
const buildGraph = (nodes, links) => {
  const graph = {}
  nodes.forEach(node => {
    graph[node.name] = []
  })
  
  links.forEach(link => {
    if (!graph[link.source]) graph[link.source] = []
    graph[link.source].push(link.target)
  })
  
  return graph
}

// 检测并打断循环
const detectAndBreakCycles = (graph, links) => {
  const visited = {}
  const recursionStack = {}
  
  const hasCycle = (node) => {
    if (!visited[node]) {
      visited[node] = true
      recursionStack[node] = true
      
      const neighbors = graph[node] || []
      for (const neighbor of neighbors) {
        if (!visited[neighbor] && hasCycle(neighbor)) {
          return true
        } else if (recursionStack[neighbor]) {
          // 找到循环，移除这条边
          const index = links.findIndex(
            l => l.source === node && l.target === neighbor
          )
          if (index !== -1) {
            links.splice(index, 1)
          }
          return true
        }
      }
    }
    recursionStack[node] = false
    return false
  }
  
  Object.keys(graph).forEach(node => {
    hasCycle(node)
  })
}

onMounted(() => {
  fetchSankeyData()
})
<\script>

<template>
  <div class="categories">
    <h2>分类数据分析</h2>
    
    <el-card shadow="hover" class="filter-card">
      <CategoryTree v-model="selectedCategory" />
    </el-card>
    
    <div class="chart-row">
      <el-card shadow="hover" class="chart-card">
        <template #header>
          <div class="card-header">
            <span>分类流转路径</span>
          </div>
        </template>
        <SankeyChart :data="sankeyData" title="分类流转路径" />
      </el-card>
      
      <el-card shadow="hover" class="chart-card">
        <template #header>
          <div class="card-header">
            <span>分类关键词</span>
            <span v-if="selectedCategory.level1">当前分类: {{ selectedCategory.level1 }}</span>
          </div>
        </template>
        <WordCloud 
          :data="keywords" 
          :title="selectedCategory.level1 ? `${selectedCategory.level1}关键词` : '请选择分类'" 
        />
      </el-card>
    </div>
    
    <el-card shadow="hover" class="health-card">
      <template #header>
        <div class="card-header">
          <span>分类健康度评分</span>
          <span v-if="selectedCategory.level1">当前分类: {{ selectedCategory.level1 }}</span>
        </div>
      </template>
      
      <div v-if="selectedCategory.level1" class="health-content">
        <div class="score">
          <el-progress 
            type="dashboard" 
            :percentage="healthData.health_score * 10" 
            :width="150"
            :color="healthData.health_score >= 8 ? '#67C23A' : 
                   healthData.health_score >= 5 ? '#E6A23C' : '#F56C6C'"
          >
            <template #default>
              <div class="score-value">
                <span class="number">{{ healthData.health_score.toFixed(1) }}</span>
                <span class="total">/10</span>
              </div>
            </template>
          </el-progress>
          <div class="score-label">综合评分</div>
        </div>
        
        <div class="metrics">
          <div v-for="(metric, index) in healthData.metrics" :key="index" class="metric-item">
            <div class="metric-name">{{ metric.name }}</div>
            <div class="metric-value">{{ metric.value }}</div>
          </div>
        </div>
      </div>
      
      <div v-else class="empty-tip">
        请选择分类查看健康度评分
      </div>
    </el-card>
  </div>
</template>

<style scoped>
.categories {
  padding: 20px;
}

.filter-card {
  margin-bottom: 20px;
}

.chart-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  margin-bottom: 20px;
}

.chart-card {
  margin-bottom: 20px;
}

.health-card {
  margin-bottom: 20px;
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.health-content {
  display: flex;
  align-items: center;
  padding: 20px;
}

.score {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-right: 50px;
}

.score-value {
  text-align: center;
}

.score-value .number {
  font-size: 24px;
  font-weight: bold;
}

.score-value .total {
  font-size: 14px;
  color: #999;
}

.score-label {
  margin-top: 10px;
  font-size: 16px;
  color: #666;
}

.metrics {
  flex: 1;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
}

.metric-item {
  background-color: #f5f7fa;
  padding: 15px;
  border-radius: 4px;
}

.metric-name {
  font-size: 14px;
  color: #909399;
  margin-bottom: 10px;
}

.metric-value {
  font-size: 20px;
  font-weight: bold;
  color: #303133;
}

.empty-tip {
  text-align: center;
  padding: 50px 0;
  color: #909399;
  font-size: 16px;
}
</style>